package com.tarena.lbs.coupon.service.impl;

import com.alibaba.nacos.common.utils.StringUtils;
import com.github.pagehelper.PageInfo;
import com.tarena.lbs.activity.api.ActivityApi;
import com.tarena.lbs.activity.api.pojo.ActivityDto;
import com.tarena.lbs.activity.pojo.po.ActivityPO;
import com.tarena.lbs.activity.rpc.ActivityApiImpl;
import com.tarena.lbs.attach.api.AttachApi;
import com.tarena.lbs.attach.pojo.dto.AttachQrDTO;
import com.tarena.lbs.attach.pojo.param.AttachQRParam;
import com.tarena.lbs.base.common.utils.Asserts;
import com.tarena.lbs.base.protocol.exception.BusinessException;
import com.tarena.lbs.base.protocol.pager.PageResult;
import com.tarena.lbs.common.enums.BusinessTypeEnum;
import com.tarena.lbs.common.principle.UserPrinciple;
import com.tarena.lbs.common.utils.AuthenticationContextUtils;
import com.tarena.lbs.coupon.api.CouponApi;
import com.tarena.lbs.coupon.api.pojo.param.UserCouponsParam;
import com.tarena.lbs.coupon.dao.repository.*;
import com.tarena.lbs.coupon.dao.repository.CouponCodeRepository;
import com.tarena.lbs.coupon.dao.repository.CouponRepository;
import com.tarena.lbs.coupon.dao.repository.UserCouponsRepository;
import com.tarena.lbs.coupon.pojo.param.CouponParam;
import com.tarena.lbs.coupon.pojo.po.CouponCodePO;
import com.tarena.lbs.coupon.pojo.po.CouponPO;
import com.tarena.lbs.coupon.pojo.po.CouponStockPO;
import com.tarena.lbs.coupon.pojo.po.UserCouponsPO;
import com.tarena.lbs.coupon.pojo.query.CouponQuery;
import com.tarena.lbs.coupon.pojo.query.UserCouponCodeQuery;
import com.tarena.lbs.coupon.pojo.query.UserCouponQuery;
import com.tarena.lbs.coupon.pojo.vo.CouponVO;
import com.tarena.lbs.coupon.pojo.vo.UserCouponsVO;
import com.tarena.lbs.coupon.service.CouponService;
import com.tarena.lbs.coupon.utils.CacheUtils;
import com.tarena.lbs.user.api.UserApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import com.alibaba.nacos.common.utils.CollectionUtils;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CouponServiceImpl implements CouponService {
    @Autowired
    private CouponRepository couponRepository;
    @Autowired
    private CacheUtils cacheUtils;
    @DubboReference
    private ActivityApi activityApi;
    @Autowired
    private StockRepository stockRepository;
    @DubboReference
    private AttachApi attachApi;
    @DubboReference
    private UserApi userApi;
    @Autowired
    private CouponCodeRepository couponCodeRepository;

    @Override
    public  void insert(CouponParam couponParam) throws BusinessException {
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        log.info("userPrinciple:{}", userPrinciple);
        //断言异常
        Asserts.isTrue(userPrinciple == null, new BusinessException("-2", "认证解析错误"));
        Integer id = userPrinciple.getId();
        Integer businessId = userApi.getAdminById(id).getBusinessId();
        log.info("couponParam:{}",couponParam);


        CouponPO couponPO = new CouponPO();
        BeanUtils.copyProperties(couponParam,couponPO);
//        couponPO.setStartDate(new Date());
//        couponPO.setEndDate(new Date());
        // 定义日期时间格式模板
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd H:m:s");
        // 解析字符串为 LocalDateTime 对象
        LocalDateTime localDateTime = LocalDateTime.parse(couponParam.getStartDate(), formatter);
        LocalDateTime localDateTime1 = LocalDateTime.parse(couponParam.getEndDate(), formatter);
        // 将 LocalDateTime 转换为 Date
        Date date = Date.from(localDateTime.atZone(java.time.ZoneId.systemDefault()).toInstant());
        Date date1 = Date.from(localDateTime1.atZone(java.time.ZoneId.systemDefault()).toInstant());
        couponPO.setStartDate(date);
        couponPO.setEndDate(date1);
        couponPO.setCreateAt(new Date());
        couponPO.setUpdateAt(new Date());
        // 获取时间对象
        Date createAt = couponPO.getCreateAt();
        Date endDate = couponPO.getEndDate();
        Date startDate = couponPO.getStartDate();
// 用当前时间判断优惠券状态（更常用）
        Date now = new Date();

        if (now.after(endDate)) {
            couponPO.setStatus(2); // 已过期
        } else if (now.before(startDate)) {
            couponPO.setStatus(0); // 未开始
        } else {
            couponPO.setStatus(1); // 有效期内
        }
        couponPO.setEnableStatus(1);
        couponPO.setBusinessId(businessId);
        couponRepository.insert(couponPO);
        CouponStockPO couponStockPO = new CouponStockPO();
        couponStockPO.setCouponId(couponPO.getId());
        couponStockPO.setNum(couponPO.getUsageNum());
        couponStockPO.setCreateAt(new Date());
        couponStockPO.setUpdateAt(new Date());
        couponStockPO.setBusinessId(businessId);
        stockRepository.insert(couponStockPO);
        for (int i = 0; i < couponPO.getUsageNum(); i++){
            CouponCodePO couponCodePO = new CouponCodePO();
            couponCodePO.setCouponId(couponPO.getId());
            couponCodePO.setCouponCode(UUID.randomUUID().toString());
            couponCodePO.setStatus(1);
            couponCodePO.setBusinessId(businessId);
            couponCodePO.setCreateAt(new Date());
            couponCodePO.setUpdateAt(new Date());
            couponCodeRepository.insert(couponCodePO);
        }

    }
    @Override
    public PageResult<CouponVO> pageList(CouponQuery query) throws BusinessException {
        PageResult<CouponVO> voPage = new PageResult<>();
        //查询当前管理员信息
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        log.info("userPrinciple:{}", userPrinciple);
        //断言异常
        Asserts.isTrue(userPrinciple == null, new BusinessException("-2", "认证解析错误"));
        Integer id = userPrinciple.getId();
        Integer businessId = userApi.getAdminById(id).getBusinessId();
        query.setBusinessId(businessId);
        log.info("优惠券查询条件query:{}",query);
        PageInfo<CouponPO> pageInfo = couponRepository.pagesList(query);
        voPage.setPageNo(pageInfo.getPageNum());
        voPage.setPageSize(pageInfo.getPageSize());
        voPage.setTotal(pageInfo.getTotal());
        List<CouponVO> list = null;
        List<CouponPO> pos = pageInfo.getList();
        if (CollectionUtils.isNotEmpty(pos)){
            list = pos.stream().map(po->{
                CouponVO vo = new CouponVO();
                BeanUtils.copyProperties(po,vo);
                return vo;
            }).collect(Collectors.toList());
        }
        voPage.setObjects(list);
        return voPage;
    }
    @Override
    public void update(CouponParam couponParam){
        CouponPO couponPO = new CouponPO();
        BeanUtils.copyProperties(couponParam,couponPO);
        // 定义日期时间格式模板
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd H:m:s");
        // 解析字符串为 LocalDateTime 对象
        LocalDateTime localDateTime = LocalDateTime.parse(couponParam.getStartDate(), formatter);
        LocalDateTime localDateTime1 = LocalDateTime.parse(couponParam.getEndDate(), formatter);
        // 将 LocalDateTime 转换为 Date
        Date date = Date.from(localDateTime.atZone(java.time.ZoneId.systemDefault()).toInstant());
        Date date1 = Date.from(localDateTime1.atZone(java.time.ZoneId.systemDefault()).toInstant());
        couponPO.setStartDate(date);
        couponPO.setEndDate(date1);
        couponRepository.update(couponPO);

    }
    @Override
    public CouponVO select(Integer id){
        // 1.定义缓存键（推荐格式：业务前缀:ID）
       String cacheKey = "coupon:" + id;

        // 2.查询缓存
        Serializable couponData = cacheUtils.get(cacheKey);
        if (couponData != null) {
            // 3.缓存存在，直接返回
            log.info("数据来自Redis缓存couponData:{}",couponData);
            return (CouponVO) couponData;
        }
         //4.缓存不存在，查询数据库
        CouponVO couponVO = couponRepository.selectById(id);
        if (couponVO == null) {
            return null;
        }
        // 5.将数据库查询结果存入缓存（设置过期时间，如10分钟）
        log.info("数据来自MySQL数据库couponVO:{}",couponVO);
        cacheUtils.setExpire(cacheKey,couponVO, 600);
    return couponVO;
    }

//    // 引入用户和活动相关的API
//    @DubboReference
//    private ActivityApi activityApi;
//    @DubboReference
//    private UserApi userApi;
//    @Autowired
//    private RedisTemplate <String, Serializable> redisTemplate;
//    @Override
//    @Transactional(rollbackFor = Exception.class)
//    public void receiveCoupon(UserCouponsParam param) throws BusinessException{
//        // 1. 用户验证 - 解析token获取用户id并与入参比对
//        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
//        log.info("获取用户的身份信息为userPrinciple:{}",userPrinciple);
//        Asserts.isTrue(userPrinciple == null, new BusinessException("-2", "认证解析错误"));
//        Integer tokenUserId = userPrinciple.getId();
//        Integer paramUserId = param.getUserId();
//        Asserts.isTrue(!tokenUserId.equals(paramUserId), new BusinessException("-3", "用户ID不匹配，禁止代领"));
//
//        // 2. 验证活动
//        Integer activityId = param.getActivityId();
//        if (activityId != null) {
//            ActivityDto activityDto = activityApi.getActivityById(activityId);
//            Asserts.isTrue(activityDto == null, new BusinessException("-4", "活动不存在"));
//
//            // 验证活动有效期
//            Date now = new Date();
//            Asserts.isTrue(now.after(activityDto.getEndDate()), new BusinessException("-5", "活动已结束"));
//
//            // 验证活动状态
//            Asserts.isTrue(activityDto.getEnableStatus() != 1, new BusinessException("-6", "活动未启用"));
//
//            // 验证用户是否是活动的目标用户
//            Boolean isVisible = activityApi.activityVisible(tokenUserId, activityId);
//            Asserts.isTrue(!isVisible, new BusinessException("-7", "您不符合活动参与条件"));
//        }
//
//        // 3. 验证优惠券
//        Integer couponId = param.getCouponId();
//        CouponVO couponVO = this.select(couponId);
//        Asserts.isTrue(couponVO == null, new BusinessException("-8", "优惠券不存在"));
//
//        // 验证优惠券状态
//        Asserts.isTrue(couponVO.getEnableStatus() != 1, new BusinessException("-9", "优惠券未启用"));
//
//        // 4. 验证用户领取上限
//        // 检查用户是否已经领取过该优惠券（这里假设每个用户只能领取一次）
//        String userCouponKey = "user:coupon:" + tokenUserId + ":" + couponId;
//        Boolean hasReceived = (Boolean) cacheUtils.get(userCouponKey);
//        if (hasReceived == null) {
//            // 这里应该查询数据库检查用户是否已经领取过该优惠券
//            // 简化实现，假设数据库检查结果
//            hasReceived = false;
//            if (!hasReceived) {
//                cacheUtils.setExpire(userCouponKey, true, 86400); // 缓存一天
//            }
//        }
//        Asserts.isTrue(hasReceived, new BusinessException("-10", "您已经领取过该优惠券"));
//
//        // 5. 实现重试机制
//        int maxRetryTimes = 3;
//        int retryCount = 0;
//        boolean success = false;
//
//        while (retryCount < maxRetryTimes && !success) {
//            try {
//                // 如果不是第一次尝试，等待一段时间再重试
//                if (retryCount > 0) {
//                    Thread.sleep(100 * retryCount); // 递增等待时间
//                }
//
//                // 6. 检查库存
//                String stockKey = "coupon:stock:" + couponId;
//                Integer stock = (Integer) cacheUtils.get(stockKey);
//                if (stock == null) {
//                    // 从数据库加载库存
//                    // 简化实现，假设数据库查询结果
//                    stock = 100; // 假设初始库存为100
//                    cacheUtils.set(stockKey, stock);
//                }
//                Asserts.isTrue(stock <= 0, new BusinessException("-11", "优惠券已领完"));
//
//                // 7. 实现分布式锁
//                String lockKey = "receive:coupon:" + couponId;
//                String requestId = UUID.randomUUID().toString();
//                boolean tryLock = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, 5, TimeUnit.SECONDS);
//
//                if (tryLock) {
//                    try {
//                        // 再次检查库存，防止在获取锁期间库存被其他线程扣减
//                        stock = (Integer) cacheUtils.get(stockKey);
//                        Asserts.isTrue(stock <= 0, new BusinessException("-11", "优惠券已领完"));
//
//                        // 8. 扣减库存
//                        cacheUtils.set(stockKey, stock - 1);
//
//                        // 9. 生成优惠券码
//                        String couponCode = generateCouponCode();
//
//                        // 10. 保存用户领取记录
//                        UserCouponsPO userCouponsPO = new UserCouponsPO();
//                        userCouponsPO.setUserId(tokenUserId);
//                        userCouponsPO.setCouponId(couponId);
//                        userCouponsPO.setReceiveChannel(param.getReceiveChannel());
//                        userCouponsPO.setStatus(0); // 0表示未使用
//                        userCouponsPO.setCreateAt(new Date());
//                        userCouponsPO.setUpdateAt(new Date());
//                        userCouponsPO.setActivityId(activityId);
//                        userCouponsPO.setCouponCode(couponCode);
//                        userCouponsPO.setCouponType(couponVO.getCouponType());
//                        userCouponsPO.setCouponValue(couponVO.getDiscountValue());
//                        userCouponsPO.setShopId(param.getShopId());
//
//                        // 这里应该调用UserCouponsRepository保存用户领取记录
//                        // 简化实现，假设保存成功
//                        log.info("用户:{} 领取优惠券:{} 成功，优惠券码:{} 库存剩余:{}",
//                                tokenUserId, couponId, couponCode, stock - 1);
//
//                        success = true;
//                    } finally {
//                        // 释放锁，只能释放自己加的锁
//                        String currentValue = (String) redisTemplate.opsForValue().get(lockKey);
//                        if (requestId.equals(currentValue)) {
//                            // 使用Lua脚本保证原子性
//                            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
//                            redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
//                                    Collections.singletonList(lockKey), requestId);
//                        }
//                    }
//                } else {
//                    // 获取锁失败，增加重试次数
//                    retryCount++;
//                    if (retryCount >= maxRetryTimes) {
//                        throw new BusinessException("-12", "当前领取人数过多，请稍后再试");
//                    }
//                }
//            } catch (InterruptedException e) {
//                Thread.currentThread().interrupt();
//                throw new BusinessException("-13", "领取过程被中断");
//            }
//        }
//    }
//
//    // 生成优惠券码的辅助方法
//    private String generateCouponCode() {
//        // 简化实现，实际应该生成更复杂的唯一码
//        return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16).toUpperCase();
//    }



    @Autowired
    private UserCouponsRepository userCouponsRepository;
    @DubboReference
    private CouponApi couponApi;

    @Autowired
    private RedisTemplate redisTemplate;

    @Transactional(rollbackFor = Exception.class)
    public void receiveCoupon(UserCouponsParam param) throws BusinessException {
        // 1. 拿到认证的 userId 也是解析 认证检验
        final Integer userId;
        if (param.getUserId() == null) {
            userId = getUserId();
        } else {
            userId = param.getUserId();
        }

        // 2. 校验活动和用户 是否合法,如果合法 返回活动对象 以活动对象是否为空 判断是否合法
        ActivityPO activity = checkUserAndActivity(param.getActivityId(), userId);
        CouponPO coupon = checkUserAndCoupon(param.getCouponId(), userId);
        log.info("coupon:{}",coupon.toString());
        Asserts.isTrue(activity == null, new BusinessException("-2", "活动校验失败"));
        Asserts.isTrue(coupon == null, new BusinessException("-2", "优惠券校验失败"));

        //获取couponCode
        String couponCode = couponCodeRepository.getCouponCode(Integer.valueOf(coupon.getId()));
        // 3. 开始 TCC 事务
        tccTransaction(param, userId, activity, coupon,couponCode);
    }



    @Override
    public UserCouponsVO receiveDetail(UserCouponCodeQuery query) throws BusinessException{
        // 1. 校验参数
        Asserts.isTrue(query == null || StringUtils.isBlank(query.getCouponCode()),
                new BusinessException("-2", "优惠券券码不能为空"));

        // 2. 根据优惠券码查询用户优惠券信息
        UserCouponsPO userCouponsPO = userCouponsRepository.findByCouponCode(query.getCouponCode());
        Asserts.isTrue(userCouponsPO == null, new BusinessException("-2", "优惠券不存在"));

        // 3. 校验用户ID是否匹配（如果查询中提供了用户ID）
        if (query.getUserId() != null && !query.getUserId().equals(userCouponsPO.getUserId())) {
            throw new BusinessException("-2", "用户与优惠券不匹配");
        }

        // 4. 查询优惠券信息
        CouponPO couponPO = couponRepository.getCouponById(userCouponsPO.getCouponId());
        Asserts.isTrue(couponPO == null, new BusinessException("-2", "优惠券信息不存在"));

        // 5. 生成二维码
        // 构建二维码内容（可以是优惠券详情页的链接或其他需要展示的信息）
        String qrContent = "coupon_code=" + query.getCouponCode() + "&user_id=" + userCouponsPO.getUserId();

        // 设置二维码参数
        AttachQRParam qrParam = new AttachQRParam();
        qrParam.setContent(qrContent);
        qrParam.setBusinessType(BusinessTypeEnum.COUPON_CODE.getType()); // 1表示优惠券业务类型
        qrParam.setBusinessId(userCouponsPO.getId()); // 使用用户优惠券ID作为业务ID

        // 调用attach模块生成二维码
        AttachQrDTO qrDTO = attachApi.generateQrCode(qrParam);

        // 6. 构建返回对象
        UserCouponsVO userCouponsVO = new UserCouponsVO();
        BeanUtils.copyProperties(userCouponsPO, userCouponsVO);
        userCouponsVO.setCouponName(couponPO.getCouponName());

        // 设置二维码地址
        if (qrDTO != null) {
            userCouponsVO.setCouponUrl(qrDTO.getUrl());
        }

        return userCouponsVO;
    }

    @Override
    public PageResult<UserCouponsVO> receiveList(UserCouponQuery query) throws BusinessException {
        log.info("查询用户领取优惠券列表, query={}", query);

        // 校验参数
        Asserts.isTrue(query == null, new BusinessException("-2", "查询参数不能为空"));
        Asserts.isTrue(query.getUserId() == null, new BusinessException("-2", "用户ID不能为空"));

        // 设置默认分页参数
        if (query.getPageNo() == null || query.getPageNo() <= 0) {
            query.setPageNo(1);
        }
        if (query.getPageSize() == null || query.getPageSize() <= 0) {
            query.setPageSize(10);
        }

        // 调用repository查询数据
        PageInfo<UserCouponsPO> pageInfo = userCouponsRepository.pagesList(query);

        // 构建返回结果
        PageResult<UserCouponsVO> pageResult = new PageResult<>();
        pageResult.setPageNo(pageInfo.getPageNum());
        pageResult.setPageSize(pageInfo.getPageSize());
        pageResult.setTotal(pageInfo.getTotal());
        // PO转换为VO
        List<UserCouponsVO> vos = null;
        List<UserCouponsPO> pos = pageInfo.getList();
        if (CollectionUtils.isNotEmpty(pos)) {
            vos = pos.stream().map(po -> {
                UserCouponsVO vo = new UserCouponsVO();
                BeanUtils.copyProperties(po, vo);

                // 查询优惠券信息，设置优惠券名称
                CouponPO couponPO = couponRepository.getCouponById(po.getCouponId());
                if (couponPO != null) {
                    vo.setCouponName(couponPO.getCouponName());
                }

                return vo;
            }).collect(Collectors.toList());
        }

        pageResult.setObjects(vos);

        return pageResult;
    }

    private void tccTransaction(UserCouponsParam param, Integer userId, ActivityPO activity, CouponPO coupon,String couponCode) throws BusinessException {
        // Try 阶段
        boolean trySuccess = tryReceiveCoupon(coupon, userId,param,couponCode);
        if (!trySuccess) {
            log.error("Try 阶段失败，事务回滚");
            return;
        }

        // Confirm 阶段
        try {
            log.debug("Confirm 阶段，入参:{}",param.toString());
            confirmReceiveCoupon(coupon, userId,couponCode);
        } catch (BusinessException e) {
            log.error("Confirm 阶段失败，执行 Cancel 阶段");
            cancelReceiveCoupon(coupon, userId);
            throw e;
        }
    }

    private boolean tryReceiveCoupon(CouponPO coupon, Integer userId,UserCouponsParam param,String couponCode) {
        // 1. 检查库存是否充足
        log.info("Try 阶段开始");
        Boolean enough = stockIsEnough(coupon.getId());
        log.info("库存充足:{}",enough);
        if (!enough) {
            log.error("库存不足，Try 阶段失败");
            return false;
        }

        /*// 2. 预留库存
        Boolean reserveResult = couponApi.reserveStock(coupon.getId(), 1);
        if (!reserveResult) {
            log.error("预留库存失败，Try 阶段失败");
            return false;
        }*/

        // 3. 记录领取信息（但不立即提交）
        //记录用户领取优惠券信息-------
        UserCouponsPO userCouponsPO = new UserCouponsPO();
        BeanUtils.copyProperties(param,userCouponsPO);
        userCouponsPO.setCouponType(coupon.getCouponType());
        userCouponsPO.setCouponValue(coupon.getDiscountValue());
        userCouponsPO.setCreateAt(new Date());
        userCouponsPO.setUpdateAt(userCouponsPO.getCreateAt());
        userCouponsPO.setUserId(userId);
        userCouponsPO.setCouponId(coupon.getId());
        userCouponsPO.setStatus(3); // 3 表示未确认
        userCouponsPO.setCouponCode(couponCode);
        log.info("Try 阶段，记录领取信息:{}",userCouponsPO.toString());
        userCouponsRepository.save(userCouponsPO);
        //记录用户领取优惠券信息-------
        log.info("Try 阶段成功，预留库存并记录领取信息");
        return true;
    }

    private void confirmReceiveCoupon(CouponPO coupon, Integer userId,String couponCode) throws BusinessException {
        // 1. 扣减库存
        Boolean reduceResult = couponApi.reduceStock(coupon.getId(), 1);
        if (!reduceResult) {
            log.error("库存扣减失败，Confirm 阶段失败");
            throw new BusinessException("-2", "库存扣减失败");
        }
        UserCouponsPO userCouponsPO = userCouponsRepository.findByCouponCode(couponCode);

        // 2. 更新领取记录状态为已确认
        //UserCouponsPO userCouponsPO = userCouponsRepository.findByUserIdAndCouponId(userId, coupon.getId());
        if (userCouponsPO != null) {
            userCouponsPO.setStatus(0); // 0 表示已确认
            userCouponsRepository.update(userCouponsPO);
        }

        //3.更新coupon_code状态为已使用
        log.info("couponCode:{}",couponCode);
        if (userCouponsPO == null) {
            log.error("用户领取记录不存在，Confirm 阶段失败");
            throw new BusinessException("-3", "用户领取记录不存在");
        }
        CouponCodePO couponCodePO = couponCodeRepository.getByCouponCode(couponCode);
        if (couponCodePO != null) {
            couponCodePO.setStatus(0); // 0 表示已使用
            couponCodeRepository.update(couponCodePO);
        }
        log.info("Confirm 阶段成功，扣减库存并更新领取状态");
    }

    private void cancelReceiveCoupon(CouponPO coupon, Integer userId) throws BusinessException{
        // 1. 释放预留库存
//        Boolean releaseResult = couponApi.releaseStock(coupon.getId(), 1);
//        if (!releaseResult) {
//            log.error("释放库存失败，Cancel 阶段失败");
//            throw new BusinessException("-2", "释放库存失败");
//        }

        // 2. 删除领取记录
        UserCouponsPO userCouponsPO = userCouponsRepository.findByUserIdAndCouponId(userId, coupon.getId());
        if (userCouponsPO != null) {
            log.info("即将删除领取记录:{}",userCouponsPO.toString());
            userCouponsRepository.delete(userCouponsPO);
        }

        log.info("Cancel 阶段成功，释放库存并删除领取记录");
    }

    private Boolean stockIsEnough(Integer couponId) {
        // 1. 使用库存接口类 查询剩余库存数量
        Integer num = couponApi.getCouponStock(couponId);
        log.info("检查");
        // 2. 判断是否 > 0
        return num > 0;
    }

    private CouponPO checkUserAndCoupon(Integer couponId, Integer userId) {
        // 1. 先检查一下 coupon
        CouponPO po = checkCoupon(couponId);
        // 2. 检查用户是否已经领取 coupon 到上限了
        if (po != null) {
            try {
                checkUserLimit(po, userId);
            } catch (Exception e) {
                log.error("用户领取上限检查失败");
                return null;
            }
        }
        return po;
    }

    private void checkUserLimit(CouponPO po, Integer userId) throws BusinessException {
        log.info("检查用户userId:{}领取优惠券couponId:{}", userId, po.getId());
        // 检查当前 userId 领取这个 coupon 的次数是否达到 coupon 上限
        Integer couponId = po.getId();
        Integer usageLimit = po.getUsageLimit(); // 领取上限
        log.info("领取上限:{}", usageLimit);
        // 1. 查询该用户 领取该优惠券的次数 user_coupons 领取记录表 count
        Long count = userCouponsRepository.countReceiveCoupons(couponId, userId);
        log.info("领取次数:{}", count);
        // 2. 判断是否已经等于 usageLimit 如果已经等于 不能再领了 如果小于可以在领
        if (usageLimit.equals(count.intValue())) {
            log.info("当前用户:{},领取优惠券:{},已达:{}次,不可在领取", userId, po.getId(), count);
            throw new BusinessException("-2", "优惠券领取上限");
        }
    }

    private CouponPO checkCoupon(Integer couponId) {
        // 1. 查询是否可以查询到对象
        log.info("检查优惠券couponId:{}", couponId);
        CouponPO po = couponRepository.getCouponById(couponId);
        log.info("检查优惠券couponId:{}查询结果:{}", couponId, po);
        if (po == null) {
            return null;
        }
        // 2. 状态
        Integer status = po.getStatus();
        Integer enableStatus = po.getEnableStatus();
        if (status == null || enableStatus == null) {
            log.error("优惠券状态不正常 检查失败,status:{},enableStatus:{}", status, enableStatus);
            return null;
        }
        if (status != 1 || enableStatus != 1) {
            log.error("优惠券状态不正常 检查失败,status:{},enableStatus:{}", status, enableStatus);
            return null;
        }
        return po;
    }

    private ActivityPO checkUserAndActivity(Integer activityId, Integer userId) {
        // 先检查活动
        ActivityPO activity = checkActivity(activityId);
        log.info("检查活动activity:{}", activity);
        // 检查互动目标人群
        if (activity != null) {
            // 说明活动检查是成功的
            try {
                checkTargetCustomer(activity.getTargetCustomer(), userId, activity.getBusinessId());
            } catch (BusinessException e) {
                log.error("用户不属于目标人群");
                return null;
            }
        }
        return activity;
    }

    private void checkTargetCustomer(String targetCustomer, Integer userId, Integer businessId) throws BusinessException {
        // 1. 使用 userId 查询 所属的人群
        List<Integer> userGroupIds = userApi.getUserGroupIds(userId, businessId);
        log.info("检查用户userId:{}所属人群:{}", userId, userGroupIds);
        // 2. 判断目标人群是否在范围内
        if (CollectionUtils.isNotEmpty(userGroupIds)) {
            log.info("用户所属人群不为空,groupIds:{}", userGroupIds);
            Integer targetGroupId = Integer.valueOf(targetCustomer);
            boolean contains = userGroupIds.contains(targetGroupId);
            if (!contains) {
                log.info("用户所属人群:{},不包含活动目标人群:{}", userGroupIds, targetGroupId);
                throw new BusinessException("-2", "用户不属于活动目标人群");
            }
        } else {
            log.info("用户所属人群为空");
            throw new BusinessException("-2", "用户不属于活动目标人群");
        }
    }

    private ActivityPO checkActivity(Integer activityId) {
        log.info("检查活动activityId:{}", activityId);
        // 1. 活动得存在 非空 读取查询活动数据
        ActivityDto po = activityApi.getActivityById(activityId);
        if (po == null) {
            log.error("活动数据为空,校验活动失败");
            return null;
        }
        // 2. 活动时间 和当前系统时间 对得上 开始时间< 系统时间 <结束时间
        Date startDate = po.getStartDate();
        Date endDate = po.getEndDate();
        Date now = new Date();
        if (startDate == null || endDate == null) {
            log.error("活动开始时间:{},结束时间:{},有一个为空", startDate, endDate);
            return null;
        }
        if (now.before(startDate) || now.after(endDate)) {
            log.error("活动时间不正常,开始时间:{},结束时间:{},系统时间:{}", startDate, endDate, now);
            return null;
        }
        // 3. 活动状态 满足要求 待激活 待推广 启用状态 status==0 enableStatus==0
        Integer status = po.getStatus();
        Integer enableStatus = po.getEnableStatus();
        if (status == null || enableStatus == null) {
            log.error("活动状态为空,status:{},enableStatus:{}", status, enableStatus);
            return null;
        }
        if (status != 0 || enableStatus != 0) {
            log.error("活动状态值错误,status:{},enableStatus:{}", status, enableStatus);
            return null;
        }
        // 4. 检查一下 后续业务要用到的目标人群是否存在数据
        String targetCustomer = po.getTargetCustomer();
        if (StringUtils.isBlank(targetCustomer)) {
            log.error("活动目标人群为空");
            return null;
        }
        ActivityPO activityPO = new ActivityPO();
        BeanUtils.copyProperties(po, activityPO);
        return activityPO;
    }

    private Integer getUserId() throws BusinessException {
        UserPrinciple userPrinciple = AuthenticationContextUtils.get();
        Asserts.isTrue(userPrinciple == null, new BusinessException("-2", "用户认证解析失败"));
        return userPrinciple.getId();
    }

}
